<?php

	/**
     * Event
     *
	 * Class which represents an event in the event system
	 *
	 * @package      Core
	 * @subpackage   Event
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
	 */
	class Core_Event extends Core_Object
    {

	////////////////////////////////////////////////////////////////////////////
	//                                Variables                               //
	////////////////////////////////////////////////////////////////////////////

        /**
         * The name of the event
         *
         * @var string
         */
        protected $_name = null;

        /**
         * Event parameters
         *
         * @var array
         */
        protected $_params = null;

        /**
         * @var array of array
         */
        protected $_responses = array();

        /**
        * Because a listener can handle several events, we should
        * create only one instance of it for performance.
        * $handledEvents will contains only reference to this listener.
        *
        * @var Core_Event_Listener[]
        */
        protected static $listeners = array();

        /**
        * Hash table for handled event
        * $_hash['event'] = array of events (by reference)
        *
        * @var object[]
        */
        protected static $handledEvents = array();

	////////////////////////////////////////////////////////////////////////////
	//                            Getters / Setters                           //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Returns the name of the event
         * Note: will be used internally for optimisations
         *
         * @return       string
         */
        public function getName()
        {
            return $this->_name;
        }

        /**
         * Returns all the responses
         *
         * @return      array       array of associative array
         */
        public function getResponses()
        {
            return $this->_responses;
        }

        /**
        * Returns the given parameter
        *
        * @param        string      $name : the parameter's name
        * @throws		Core_ArgumentException
        * @throws		Core_ArgumentNullException
        * @return       mixed|null
        */
        public function getParam($name)
        {
            if ($name === null) {
				throw new Core_ArgumentNullException('name is a null reference');
			}

            if (strlen(trim($name)) == 0) {
				throw new Core_ArgumentException('name is a zero-length string');
			}

            if (isset($this->_params[$name])) {
                return $this->_params[$name];
            } else {
                return null;
            }
        }

	////////////////////////////////////////////////////////////////////////////
	//                                 Methods                                //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Constructor
         *
         * @param       string      $name : the event's name
         * @param       array       $params : an associative array which contains parameters for the listeners
         * @throws		Core_ArgumentException
         * @throws		Core_ArgumentNullException
         */
        public function __construct($name, array $params = array())
        {
            if ($name === null) {
				throw new Core_ArgumentNullException('name is a null reference');
			}

            if (strlen(trim($name)) == 0) {
				throw new Core_ArgumentException('name is a zero-length string');
			}

            $this->_name   = $name;
            $this->_params = &$params;
        }

		/**
		 * Destructor
		 */
		public function __destruct()
		{
			unset($this);
		}

        /**
        * Adds data to the responses list
        *
        * @param        array       $response : a single response
        */
        public function add(array $response)
        {
            $this->_responses[] = &$response;
        }

        /**
         * Looks in all the responses if we have a parameter having value as its answer
         * eg, we want to know if we have failed = true, we do
         *
         * @param       string      $name : the param we're looking for
         * @param       mixed       $value : the value we're looking for
         * @param       mixed       $response : the response that have this value
         * @throws		Core_ArgumentException
         * @throws		Core_ArgumentNullException
         * @return      boolean     true if the response value was found
         */
        public function inResponse($name, $value, &$response)
        {
            if ($name === null) {
				throw new Core_ArgumentNullException('name is a null reference');
			}

            if (strlen(trim($name)) == 0) {
				throw new Core_ArgumentException('name is a zero-length string');
			}

            if ($value === null) {
				throw new Core_ArgumentNullException('value is a null reference');
			}

            if (strlen(trim($value)) == 0) {
				throw new Core_ArgumentException('value is a zero-length string');
			}

            $founde   = false;
            $response = array();

            foreach ($this->_responses as $key => $listenerResponse) {
                if (isset($listenerResponse[$name]) && $listenerResponse[$name] == $value) {
                    $found = true;
                    $response[] = &$this->_responses[$key];
                }
            }

            return $found;
        }

	////////////////////////////////////////////////////////////////////////////
	//                             Static Methods                             //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Sends a notification to all modules
         *
         * @param       string      $name : the event's name
         * @param       array       $params : an associative array which contains parameters for the listeners
         * @throws		Core_ArgumentException
         * @throws		Core_ArgumentNullException
         *
         * @return       Core_Event
         */
        public static function raise($name, $params = array())
        {
            if ($name === null) {
				throw new Core_ArgumentNullException('name is a null reference');
			}

            if (strlen(trim($name)) == 0) {
				throw new Core_ArgumentException('name is a zero-length string');
			}

            $event = new Core_Event($name, $params);

            if (!isset(self::$handledEvents[$name])) {
                self::getEventHandlers($name);
            }

            $methodName = 'on'. ucfirst($event->getName());
            $list = &self::$handledEvents[$name];

            foreach (array_keys($list) as $key) {
                $list[$key]->{$methodName}($event);
            }

            return $event;
       }

        /**
        * Returns the list of listeners for an event
        *
        * @param        string      $event : the event name we want the listeners for
        * @return       object[]
        */
        protected static function getEventHandlers($event)
        {
            $registry = Zend_Registry::getInstance();

            if (!Zend_Registry::isRegistered('events')) {
                $registry['events'] = Core_Event_Compiler::get();
            }

            self::$handledEvents[$event] = array();

            if (isset($registry['events'][$event])) {
                foreach ($registry['events'][$event] as $listener) {
                    list($module, $listenerName) = $listener;

                    if (!isset(self::$listeners[$module][$listenerName])) {
                        $path = Core_IO_Path::combine(
                                                        PROJECT_MODULE_PATH,
                                                        ucfirst(strtolower($module)),
                                                        'Classes',
                                                        ucfirst(strtolower($listenerName)) . 'Listener.php'
                                                    );
                        if (!Core_IO_File::exists($path)) {
	                        $path = Core_IO_Path::combine(
	                                                        SERUM_MODULE_PATH,
	                                                        ucfirst(strtolower($module)),
	                                                        'Classes',
	                                                        ucfirst(strtolower($listenerName)) . 'Listener.php'
	                                                    );
                        }

                        require_once $path;
                        $className = ucfirst(strtolower($module)) . '_Classes_' . ucfirst(strtolower($listenerName)) . 'Listener';
                        self::$listeners[$module][$listenerName] = new $className();
                    }

                    self::$handledEvents[$event][] = self::$listeners[$module][$listenerName];
                }
            }
        }
    }
